/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "NodeCpp/Util/Application.h"
#include <cassert>
#include <cstdio>
#include <csignal>
#include "NodeCpp/ErrorHandler.h"
#include "NodeCpp/Util/Option.h"
using NodeCpp::Util::Option;

namespace NodeCpp {
namespace Util {

    Application *Application::SingletonInst_ = NULL;

    Application* Application::Instance(void)
    {
        NODECPP_ASSERT(SingletonInst_);
        return SingletonInst_;
    }

    Application::Application(const char* _AppName /* = "Application" */)
        : Name_(_AppName)
        , Arguemnts_()
        , Options_()
        , UpTime_(0)
        , AtExit_()
    {
        NODECPP_ASSERT(!SingletonInst_);
        SingletonInst_ = this;
    }

    Application::~Application(void)
    {

    }

    int Application::Run(int _Argc, char* _Argv[])
    {
        int _Rc = EXIT_OK;

        do
        {
            try
            {
#if defined(PLATFORM_LINUX) || defined(PLATFORM_APPLE)
                /* Ignore SIGPIPE */
                if (!HookSignal(SIGPIPE, true)) {
                    _Rc = EXIT_FAILURE;
                    break;
                }
#endif

                /* Initialize application */
                _Rc = _Initialize(_Argc, _Argv);
                if (_Rc != EXIT_OK) {
                    break;
                }
                /* Get application startup time */
                ::time(&UpTime_);
                /* execute */
                _Rc = Main(Arguemnts_);
            }
            catch (std::exception& e)
            {
                ErrorHandler::HandleException(e);
            }
            catch (...)
            {
                ErrorHandler::HandleSystemException();
            }

        } while (false);

        /* Destroy */
        _Uninitialize();
        return _Rc;
    }

    bool Application::HookSignal(int _Signal, bool _Ignore /* = false */)
    {
        if (!_Ignore) {
            return (::signal(_Signal, &(Application::_SignalHandler)) != SIG_ERR);
        } else {
            return (::signal(_Signal, SIG_IGN) != SIG_ERR);
        }
    }

    void Application::UnhookSignal(int _Signal)
    {
        NODECPP_ASSERT(::signal(_Signal, NULL) != SIG_ERR);
    }

    void Application::OnDefineOptions(OptionSet& _Options)
    {
        /* Help */
        _Options.AddOption(Option("help", "h", "Show usage")
            .IsRequired(false)
            .HasArg(false));
    }

    int Application::OnOption(const std::string& _Name, const std::string& _Arg)
    {
        if (_Name.compare("help") == 0) {
            _ShowUsage();
            return EXIT_USAGE;
        }
        return EXIT_OK;
    }

    void Application::OnSignal(int _Signal)
    {

    }

    int Application::_Initialize(int _Argc, char* _Argv[])
    {
        /* Process command line arguments */
        for (int _Arg = 1; _Arg < _Argc; ++_Arg) {
            Arguemnts_.push_back(_Argv[_Arg]);
        }
        OnDefineOptions(Options_);
        Options_.Process(Arguemnts_);
        OptionSet::const_iterator _Iter = Options_.begin();
        for (; _Iter != Options_.end(); ++_Iter)
        {
            if (!_Iter->IsValid())
            {
                if (_Iter->IsRequired()) {
                    return EXIT_USAGE;
                }
                continue;
            }

            int _Rc = OnOption(_Iter->name(), _Iter->value());
            if (_Rc != EXIT_OK) {
                return _Rc;
            }
        }

        return OnInitialize();
    }

    void Application::_Uninitialize(void)
    {
        OnUninitialize();
    }

    void Application::_ShowUsage(void)
    {
        printf("%s USAGE\n", Name_);
        OptionSet::const_iterator _Iter = Options_.begin();
        for (; _Iter != Options_.end(); ++_Iter)
        {
            printf("--%s,\t-%s\t\t\t%s\n",
                _Iter->name().c_str(), _Iter->GetShortName().c_str(), _Iter->description().c_str());
        }
        printf("\n");
    }

    void Application::_SignalHandler(int _Signal)
    {
        Instance()->OnSignal(_Signal);
    }

}}
